/* ********************************************************
 *
 *            The Win32 console wrapper
 *            Written by `egnaro` 2011
 *
 ******************************************************** */



#ifndef	__w32console_h__
#define	__w32console_h__

#include <windows.h>
#include <cstdio>
#include <cmath>

namespace W32Console
{
	void Print(const TCHAR* text);

	namespace __util
	{
		size_t strlength(const TCHAR* str)
		{
			if (!str)
				return 0;

#ifdef	UNICODE
			return wcslen(str);
#else	// UNICODE
			return strlen(str);
#endif	// UNICODE
		}

		TCHAR* strcopy(TCHAR* dest, const TCHAR* source)
		{
#ifdef	UNICODE
			return wcscpy(dest, source);
#else	// UNICODE
			return strcpy(dest, source);
#endif	// UNICODE
		}

		void repeatstr(const TCHAR* str, int numRep)
		{
			for (int i = 0; i < numRep; ++i)
				Print(str);
		}
	};
};

namespace W32Console
{
	void ShowCursor(bool show)
	{
		CONSOLE_CURSOR_INFO cciCursor;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleCursorInfo(hStdOut, &cciCursor))
		{
			cciCursor.bVisible = static_cast<BOOL>(show);
			SetConsoleCursorInfo(hStdOut, &cciCursor);
		}
	}

	void SetCursorPosition(int x, int y)
	{
		COORD coord = {x, y};
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		SetConsoleCursorPosition(hStdOut, coord);
	}

	void GetCursorPosition(int& x, int& y)
	{
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
		{
			x = csbi.dwCursorPosition.X;
			y = csbi.dwCursorPosition.Y;
		}
		else
			x = y = 0;
	}

	void Clear(void)
	{
		COORD coord = {0, 0};
		DWORD count;
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
		{
			FillConsoleOutputCharacter(hStdOut, (TCHAR)0x20, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
			FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, coord, &count );
			SetConsoleCursorPosition(hStdOut, coord);
		}
	}

	void SetForegroundColor(int color)
	{
		WORD wColor;
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
		{
			wColor = (csbi.wAttributes & 0xf0) | (color & 0x0f);
			SetConsoleTextAttribute(hStdOut, wColor);
		}
	}

	int GetForegroundColor(void)
	{
		int color = 0;
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
			color = csbi.wAttributes & 0x0f;

		return color;
	}

	void SetBackgroundColor(int color)
	{
		WORD wColor;
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
		{
			wColor = ((color & 0x0f) << 4) | (csbi.wAttributes & 0x0f);
			SetConsoleTextAttribute(hStdOut, wColor);
		}
	}

	int GetBackgroundColor(void)
	{
		int color = 0;
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
			color = (csbi.wAttributes & 0xf0) >> 4;

		return color;
	}

	int Print_n(const TCHAR* text, int len)
	{
		DWORD count;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		WriteConsole(hStdOut, text, len, &count, NULL);
		return static_cast<int>(count);
	}

	void Print(const TCHAR* text)
	{
		Print_n(text, __util::strlength(text));
	}


	/***********************************
	 *
	 *  egnaro - console progressbar ;)
	 *
	 ***********************************/
	typedef struct _s_ProgressbarCtx
	{
		int			initialized;
		TCHAR*		title;		// title of progressbar
		int			progress;	// progress of progressbar [0-100]
		COORD		coord;		// position of progressbar
		int			titleColor;
		int			barColor;
	} ProgressbarCtx;

	int InitProgressbar(ProgressbarCtx* context, const TCHAR* title)
	{
		if (!context)
			return 0;

		memset(context, 0, sizeof(ProgressbarCtx));

		int x, y;
		GetCursorPosition(x, y);
		context->coord.X = x;
		context->coord.Y = y;

		context->titleColor = context->barColor = GetForegroundColor();

		if (title != NULL && title[0] != 0)
		{
			const size_t len = __util::strlength(title) + 1;
			context->title = new TCHAR[len];
			__util::strcopy(context->title, title);
		}

		return (context->initialized = 1);
	}

	int SetProgressbarColors(ProgressbarCtx* context, int titleColor, int barColor)
	{
		if (!context || !context->initialized)
			return 0;

		context->titleColor = titleColor;
		context->barColor = barColor;

		return 1;
	}

	int UpdateProgressbar(ProgressbarCtx* context, int progress)
	{
		static const int kMinNeededSpace = 3;	// egnaro - [] + 1 space
		static const TCHAR* chars[] = { TEXT("["), TEXT("_"), TEXT("#"), TEXT("]") };
		enum {
			kOpenBracket = 0,
			kEmpty,
			kFull,
			kCloseBracket
		};

		if (!context || !context->initialized)
			return 0;

		if (progress < 1)
			progress = 1;
		else if (progress > 100)
			progress = 100;

		CONSOLE_SCREEN_BUFFER_INFO csbi;
		HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

		if (!GetConsoleScreenBufferInfo(hStdOut, &csbi))
			return 0;

		int minPos = context->coord.X;
		int maxPos = csbi.dwSize.X;
		int coordDelta = maxPos - minPos;
		int titleLen = __util::strlength(context->title);
		int numPieces = coordDelta - titleLen;

		int oldColor = GetForegroundColor();
		SetCursorPosition(context->coord.X, context->coord.Y);

		SetForegroundColor(context->titleColor);
		Print(context->title);

		if (numPieces > kMinNeededSpace)
		{
			SetForegroundColor(context->barColor);

			numPieces -= kMinNeededSpace;

			Print_n(TEXT(" "), 1);
			Print_n(chars[kOpenBracket], 1);

			int filled = static_cast<int>(floorf((static_cast<float>(numPieces) / 100.0f) * static_cast<float>(progress)));
			__util::repeatstr(chars[kFull], filled);
			__util::repeatstr(chars[kEmpty], numPieces - filled);

			Print_n(chars[kCloseBracket], 1);
		}

		SetForegroundColor(oldColor);

		return 1;
	}

};

#endif	// __w32console_h__
